/*************************************************************************
 * @file main.go
 * @auth wuwei
 * @created_time 2018/11/ 2
 * @brief lib build cmd: "go build -buildmode=c-archive -o httplib.a main.go"
 ************************************************************************/

package main
/*
//test for enum type, not used
enum Error_type {
    error_type_ok       = 0,
    error_type_no_data  = 1,
    error_type_failed   = 2,
};
*/
import "C"

import (
	"httplib"
	"time"
	"log"
    "unsafe"
    "runtime"
)

func init() {
    runtime.GOMAXPROCS(runtime.NumCPU())
}


//////////////////// for client ///////////////

/**************************************************
* @brief used for user waiting http event, block
* @param anType 0 : client http rs
* @param anError 0 success, else something failed
* @param anUserData param callback that user set
* @param anHandle a point used to get result or request
**************************************************/
//export WaitForEvent
func WaitForEvent(anType , anError, anUserData, anHandle *int32) int {
	return httplib.WaitForEvent(anType, anError, anUserData, anHandle)
}


/**************************************************
 * @brief just test for Map type, func do nothing
 *   how to pass param for map\chan ...?
 **************************************************/
//export TestMap
func TestMap(Kv map[string]string) {
}


/**************************************************
 * @brief create a http request, it will get a handle for this request used 
 *   for user to confirm which request
 * @param astrMethod http request method, "GET", "POST"
 * @param astrUrl http request url, eg. "http://www.baidu.com",
     "http://http://120.132.38.13:29823/GetUserConfig?ClientID=1&ClientKey=123456&UserID=84494432&Type=1"
 * @param astrBody body info when "post"
 * @param callback, type of callback must be "void (*pf)(int, int, char*, int)",
 *   change it to "void*" for using. it not, it will be crash when lib call it.
 *   this way, lib will not call "WaitForEvent" to tell user response.
 *   user can also pass "NULL" and use "GetRQResult"
 *   when "WaitForEvent" has event to get data of response.
 * @return a handle mapped this request,
 *   lib use it to do operators for this request
 *   if create failed, return -1
 * @callback type "void (*pf)(int status, char* apszData, int anDataLen)";
 *   anHandle handle of request, for user release this request in lib
 *   status is the code of response statusCode(200 means ok...)
 *   apszData is char pointer for data of response, 
 *   anDataLen is length of apszData. 
 *          it maybe 0 when success because there really has no data.
 *          such as baidu, sometimes, it will response 200OK but no body data.
 **************************************************/
//export CreateRequest
func CreateRequest(astrMethod, astrUrl, astrBody *C.char, callback unsafe.Pointer) int32 {
    strMethod := C.GoString(astrMethod)
    strUrl := C.GoString(astrUrl)
    strBody := C.GoString(astrBody) 
    return httplib.CreateRequst(strMethod, strUrl, strBody, callback)
}


/**************************************************
 * @brief set http request header
 * @param anHandle handle for request
 * @param astrName header name
 * @param astrContext header context
 * @return 1:success; 0 : not find the request
 **************************************************/
//export SetHeader
func SetHeader(anHandle int32, astrName, astrContext *C.char) C.int {
    strName := C.GoString(astrName)
    strContext := C.GoString(astrContext)
    isFind := httplib.SetHeader(anHandle, strName, strContext)

    if isFind {
        return C.int(1)
    } 
    return C.int(0)
}


/**************************************************
 * @brief get http response. 
 *  call it when http event happend and no error(WaitForEvent). 
 *  Is it will call Memory Leak ?
 * @param anHandle handle of event
 * @return char point for response. 
 *  lib use C.CString to return an C.char type point, 
 *  it maybe use malloc method, 
 *  so user must call `free` to release this memory.
 **************************************************/
//export GetSResult
func GetSResult(anHandle int32) *C.char {
    result := httplib.GetSResult(anHandle)
    return C.CString(result)
}


/**************************************************
* @brief used to get requst result after get client event handle from WaitForEvent.
*   I can not return go type (such as []byte, string(go),
*   eg. "func GetRQResult(anHandle int32) string {}" or
*   "func GetRQResult(anHandle int32) []byte{}") result,
*   it will cause "panic: runtime error: cgo result has Go pointer";
*   I can not use point param to get result(such as
*   "func GetRQResult(anHandle int32, aoResult *[]byte) {}" or 
*   "func GetRQResult(anHandle int32, astrResult *string) {}"),
*   it may cause
*   "fatal error: found bad pointer in Go heap (incorrect use of unsafe or cgo?)";
* @param anHandle lib used to find event
* @return result of response
**************************************************/
func GetRQResult(anHandle int32) []byte {
    return httplib.GetResult(anHandle)
}



/**************************************************
 * @brief begin to do the request when setting request info over
 * @param anHandle handle binded with request
 **************************************************/
//export DoRequest
func DoRequest(anHandle int32) {
    httplib.DoRequest(anHandle)
}


/**************************************************
 * @brief clean resource, user must call this when deal over for event of request
 * @param anHandle handle for event deal over
 **************************************************/
//export ReleaseHandle
func ReleaseHandle(anHandle int32) {
    httplib.ReleaseHandle(anHandle)
}




////////////////////////// for svr //////////////////
 
/**************************************************
 * @brief create http server engine
 * @param astrAddress listen address, eg. "127.0.0.1:8080"
 **************************************************/
//export CreateHttpServer
func CreateHttpServer(astrAddress *C.char) {
    strAddress := C.GoString(astrAddress)
    httplib.CreateHttpServer(strAddress)
}

/**************************************************
 * @brief set router for http event
 * @param aszMethod http method, eg. "post, get,..."
 * @param aszUrl router that supported
 * @param callback type must be "int (*pfRequest) (int anHandle, char* info)"
 * @return 0 : success; else, failed
 * @callback type
 *      handle an int32 number, lib mapped it to the client request
 *      info a json type string
 *           '{"Url":"","Ip":"","Query":{"param1":"","param2":""},"Body":""}"
 *          Qeury is url params dict
 *          Body is string of request body, user need parse self
 *          if return 0 and has not called SetResponseData, lib will block
 *              until called SetResponseData.
 *          if there is no data for response, return 1
 *          if return 0 but called SetResponseData in this callback, 
 *              lib will not block, it will response to client and release request
 **************************************************/
//export SetRouter
func SetRouter(aszMethod, aszUrl *C.char, callback unsafe.Pointer) C.int {
    strMethod := C.GoString(aszMethod)
    strUrl := C.GoString(aszUrl)
    ret := httplib.SetRouter(strMethod, strUrl, callback)
    return C.int(ret)
}

/**************************************************
 * @brief run http server engine, this will block thread
 **************************************************/
//export HttpServerRun
func HttpServerRun() {
    httplib.ServerRun()
}


/**************************************************
 * @brief user set response data
 * @param anHandle lib mapped for request
 * @param anStatus to set response status, such as "200, 404, 304..."
 * @aszResponse response data
 **************************************************/
//export SetResponseData
func SetResponseData(anHandle, anStatus, anLock int32, aszResponse *C.char) {
    strResponse := C.GoString(aszResponse)
    //log.Println("main SetResult ....", anHandle, " ", anStatus, " ", strResponse)
    httplib.SetResponseData(anHandle, anStatus, anLock, strResponse)
}


/**************************************************
 * @brief add an group, so user can do something to preprocess
 *  all of those url in this group
 * @param aszUrl group name, it must begin with "/", can not use "/" as end
 * @param callback function for proprocessing those url in this group
 * @return 0 : success, else failed
 * type of callback
 *  typedef int (*pfGroup) (char* info);
 *      info a json type string,
 *           '{"Url":"","Ip":"","Query":{"param1":"","param2":""},"Body":""}"
 *      if return 0, lib will do the next operations
 *      but if not, this request will be abort and return
 **************************************************/
//export AddGroup
func AddGroup(aszUrl *C.char, callback unsafe.Pointer) C.int {
    strUrl := C.GoString(aszUrl)
    ret := httplib.AddGroup(strUrl, callback)
    return C.int(ret)
}



/**************************************************
 * @brief add url in group
 * @param aszMethod http method, eg. "POST, GET,PUT..."
 * @param aszUrl router that supported, it must begin with"/" and not use "/" as end
 * @param callback type must be "int (*pfRequest) (int anHandle, char* info)"
 *   same as SetRouter
 * @return 0 : success, else failed
 **************************************************/
//export GroupAddRouter
func GroupAddRouter(aszMethod, aszGroup, aszUrl *C.char, callback unsafe.Pointer) C.int {
    strMethod := C.GoString(aszMethod)
    strGroup := C.GoString(aszGroup)
    strUrl := C.GoString(aszUrl)

    ret := httplib.GroupAddRouter(strMethod, strGroup, strUrl, callback)
    return C.int(ret)
}


/**************************************************
 * @brief go test code
 **************************************************/
func waitthread() {
	for {
		var anType, anError, anUserData, anHandle int32
		log.Println("WaitForEvent begin")
		WaitForEvent(&anType, &anError, &anUserData, &anHandle)

		log.Println("type:", anType, " handle:", anHandle)
        //for {
            result := GetRQResult(anHandle)
            log.Println("result len:", len(result))
        //}
        ReleaseHandle(anHandle)
	}
}


func main() {

    ////////////////// test client //////////
	//go waitthread()

    //for {
    //    handle := httplib.CreateRequst("GET", "http://www.baidu.com", "", nil)
    //    //handle := CreateRequest("GET", "http://www.baidu.com", "")
    //    //handle := httplib.CreateRequst("GET", "http://120.132.38.13:29823/GetUserConfig?ClientID=1&ClientKey=123456&UserID=84494432&Type=1", "")
    //    DoRequest(handle)
    //    time.Sleep(time.Duration(3)*time.Millisecond)
    //    //time.Sleep(time.Duration(2)*time.Second)
    //}

    //////////////// test server /////////
    httplib.CreateHttpServer("120.132.38.79:8080")
    httplib.SetRouter("Post", "/eee", nil)
    httplib.SetRouter("get", "/iii/bbb", nil)
    httplib.ServerRun()  

    time.Sleep(time.Duration(2)*time.Second)
}


